package org.example.message;

import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.*;

/**
 * @author weigang
 * @create 2022-10-21 16:22
 * 简易消息队列
 */
@Slf4j
@Component
public class AsyncMessageHelper {

    public static final BlockingQueue<AsyncQueueFunction> asyncQueue = new LinkedBlockingQueue<>();


    /** ExecutorService 与 ThreadPoolExecutor 区别
    * Java.util.concurrent.ThreadPoolExecutor类是ExecutorSerivce接口的具体实现。
    * ThreadPoolExecutor使用线程池中的一个线程来执行给定的任务（Runnable或者Runnable）* */
    static ExecutorService executor = ExecutorBuilder.create()
            .setCorePoolSize(1)
            .setMaxPoolSize(4)
            .setKeepAliveTime(30, TimeUnit.SECONDS)
            .setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix("message_queue").build())
            .setWorkQueue(new LinkedBlockingQueue<>(300))
            .setHandler(new ThreadPoolExecutor.AbortPolicy())
            .build();

    @Async("messagePool")  // @Async声明异步方法
    public void sendMessage(String beanName, String method, String param) {
        try {
            //TODO 存库
            /*
             * offer方法，将任务放到阻塞队列中
             * 如果该任务已添加到此队列，则返回 true；否则返回 false
             * 只是放到这个队列中 并没有执行任务
             * */
            boolean inQueue = asyncQueue.offer(()->{

                System.out.println(method);

            });
        } catch (Exception e) {
            log.error("发送消息异常", e);
        }
    }

    @PostConstruct
    /** PostConstruct 该注解被用来修饰一个非静态的void（）方法。
    * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行，并且只会被服务器执行一次。PostConstruct在构造函数之后执行，init（）方法之前执行。
    * 通常我们会是在Spring框架中使用到@PostConstruct注解 该注解的方法在整个Bean初始化中的执行顺序：
    * Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)* */
    void systemInit() {
        //TODO 获取未执行完成的 重新入队列

        //队列执行方法
        executor.execute(() -> {
            try {
                /*
                * 通过take()方法从队列取任务，如果队列中没有任务时会调用await()阻塞队列，并不会销毁线程
                * */
                asyncQueue.take().sendMessage();
                log.info("execute complete");
            } catch (Exception e) {
                log.error("队列消费异常", e);
            }
        });
    }
}
